import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from email.header import Header
from email.utils import formataddr
from threading import Thread

class ThreadEmail(Thread):
    EMAIL_HOST = 'smtp.qq.com'  # 如果是 163 改成 smtp.163.com
    EMAIL_PORT = 587  # qq邮箱服务的端口：465、587
    EMAIL_HOST_USER = "xxx@qq.com"  # 发送邮件的邮箱帐号
    EMAIL_HOST_PASSWORD = "xxx"  # 授权码,各邮箱的设置中启用smtp服务时获取
    FROM_EMAIL_USER = EMAIL_HOST_USER  # 收件人显示发件人的邮箱
    FROM_EMAIL_NAME = '广州市xxxx技术有限公司'#设置发件人的名字，在项目使用，一般是用公司名字
    def __init__(self, receiver_email, subject, message=None, html_message=None, file_path=None,cc_email=None):
        super().__init__()
        self.receiver_email = receiver_email  # 接收人邮件
        self.cc_email = cc_email  # 邮件抄送人
        self.subject = subject  # 邮件的主题
        self.message = message  # 邮件文本内容
        self.html_message = html_message  # 邮件的html内容  注意：文本内容与html同时有的时候，html覆盖文本内容
        self.file_path = file_path  # 附件文件的路径，str 或 [str,]
        self.email = MIMEMultipart()  # 创建一个邮件对象，给这个对象添加 邮件需要的各种信息
        # 接收人邮件：必须有
        if type(receiver_email) in [list, str, tuple]:
            if type(receiver_email) in [list, tuple]:
                self.receiver_email = ','.join(receiver_email)
        else:
            raise Exception('邮件抄送人必须是字符串、列表或元组形式')

        # 抄送人邮件: 可选
        if cc_email:
            if type(cc_email) in [list, str, tuple]:
                if type(cc_email) in [list, tuple]:
                    self.cc_email = ','.join(cc_email)
            else:
                raise Exception('邮件抄送人必须是字符串、列表或元组形式')
    def run(self) -> None:
        # 1、设置邮箱对象的发送人、接收人和主题
        self.email['From'] = formataddr((self.FROM_EMAIL_NAME, self.FROM_EMAIL_USER))
        self.email['To'] = self.receiver_email
        self.email['Subject'] = Header(self.subject, 'utf-8')
        self.email['Cc'] = self.cc_email

        # 2、设置内容，如果同时attach了html信息和文本信息，文本信息会被转成附件文件。
        if not self.message and not self.html_message:
            raise Exception('发送的邮件每月携带任何内容...')
        if self.html_message:
            self.email.attach(MIMEText(self.html_message, 'html', 'utf-8'))
        else:
            self.email.attach(MIMEText(self.message, 'plain', 'utf-8'))

        # 3、邮箱的附件文件
        if self.file_path != None:
            if isinstance(self.file_path, str):
                self.file_path = [self.file_path]
            elif isinstance(self.file_path, list):
                pass
            else:
                raise Exception('邮件携带的附件，格式是文件字符串路径，就列表套文件字符串路径，不能是其他格式')
            for path in self.file_path:
                with open(path, "rb") as attachment:
                    part = MIMEBase("application", "octet-stream")
                    part.set_payload(attachment.read())
                encoders.encode_base64(part)
                part.add_header(
                    "Content-Disposition",
                    f"attachment; filename= {path}",
                )
                self.email.attach(part)

        # 4、连接smtp服务器，发送邮件
        with smtplib.SMTP(self.EMAIL_HOST, self.EMAIL_PORT) as server:
            # QQ邮箱的服务器域名和端口
            server.starttls()  # 使用 TLS 加密连接
            # 使用qq邮箱登录：邮箱号和授权码
            server.login(self.EMAIL_HOST_USER, self.EMAIL_HOST_PASSWORD)
            # 发送邮件,发送的是邮箱对象
            server.send_message(self.email)

class Email:
    EMAIL_HOST = 'smtp.qq.com'  # 如果是 163 改成 smtp.163.com
    EMAIL_PORT = 587  # qq邮箱服务的端口：465、587
    EMAIL_HOST_USER = "xxx@qq.com"  # 发送邮件的邮箱帐号
    EMAIL_HOST_PASSWORD = "xxx"  # 授权码,各邮箱的设置中启用smtp服务时获取
    FROM_EMAIL_USER = EMAIL_HOST_USER  # 收件人显示发件人的邮箱
    FROM_EMAIL_NAME = '广州市xxxx技术有限公司'  # 设置发件人的名字，在项目使用，一般是用公司名字
    def __init__(self, receiver_email, subject, message=None, html_message=None, file_path=None,cc_email=None):
        self.receiver_email = receiver_email # 接收人邮件
        self.cc_email = cc_email #邮件抄送人
        self.subject = subject  # 邮件的主题
        self.message = message  # 邮件文本内容
        self.html_message = html_message  # 邮件的html内容  注意：文本内容与html同时有的时候，html覆盖文本内容
        self.file_path = file_path  # 附件文件的路径，str 或 [str,]

        self.email = MIMEMultipart() #创建一个邮件对象,发送的邮件的信息都设置到这个对象中

        #接收人邮件：必须有
        if type(receiver_email) in [list,str,tuple]:
            if type(receiver_email) in [list,tuple]:
                self.receiver_email = ','.join(receiver_email)
        else:
            raise Exception('邮件抄送人必须是字符串、列表或元组形式')

        #抄送人邮件: 可选
        if cc_email:
            if type(cc_email) in [list,str,tuple]:
                if type(cc_email) in [list,tuple]:
                    self.cc_email = ','.join(cc_email)
            else:
                raise Exception('邮件抄送人必须是字符串、列表或元组形式')

    def start(self):
        # 1、设置邮箱对象的发送人、接收人和主题
        self.email['From'] = formataddr((self.FROM_EMAIL_NAME, self.FROM_EMAIL_USER))
        self.email['To'] = self.receiver_email
        self.email['Subject'] = Header(self.subject, 'utf-8')
        self.email['Cc'] = self.cc_email

        # 2、设置内容，如果同时attach了html信息和文本信息，文本信息会被转成附件文件。
        if not self.message and not self.html_message:
            raise Exception('发送的邮件每月携带任何内容...')
        if self.html_message:
            self.email.attach(MIMEText(self.html_message, 'html', 'utf-8'))
        else:
            self.email.attach(MIMEText(self.message, 'plain', 'utf-8'))

        # 3、邮箱的附件文件
        if self.file_path != None:
            if isinstance(self.file_path, str):
                self.file_path = [self.file_path]
            elif isinstance(self.file_path, list):
                pass
            else:
                raise Exception('邮件携带的附件，格式是文件字符串路径，就列表套文件字符串路径，不能是其他格式')
            for path in self.file_path:
                with open(path, "rb") as attachment:
                    part = MIMEBase("application", "octet-stream")
                    part.set_payload(attachment.read())
                encoders.encode_base64(part)
                part.add_header(
                    "Content-Disposition",
                    f"attachment; filename= {path}",
                )
                self.email.attach(part)

        # 4、连接smtp服务器，发送邮件
        with smtplib.SMTP(self.EMAIL_HOST, self.EMAIL_PORT) as server:
            # QQ邮箱的服务器域名和端口
            server.starttls()  # 使用 TLS 加密连接
            # 使用qq邮箱登录：邮箱号和授权码
            server.login(self.EMAIL_HOST_USER, self.EMAIL_HOST_PASSWORD)
            # 发送邮件,发送的是邮箱对象
            server.send_message(self.email)

if __name__ == '__main__':
    receiver_email = ['xxx@163.com','yyy@qq.com'] #接收人邮件账号
    cc_email = ['xxxx@163.com','yyy@qq.com'] #抄送人的邮件账号
    subject = '登录验证码' #主题
    message = '您的验证码是234523，10分钟中内有效，若非本人操作请忽视...' #文本信息
    file_path = ['1.txt', '2.txt'] #同目录下创建1.txt和2.txt 文件，测试附件文件
    html_message = '<h1>登录验证码，5分钟内有效</h1><p>您的验证码是：546783</p><p>注意：若非本人操作，建议删除邮件防止泄漏验证码信息</p>' #html信息，有这个使用这个不使用文本信息
    #1、多线程发送
    email_obj = ThreadEmail(receiver_email=receiver_email,subject=subject,message=message,file_path=file_path,html_message=html_message,cc_email=cc_email)
    email_obj.start()
    #非多线程发送
    email_obj_2 = Email(receiver_email=receiver_email,subject=subject,message=message,file_path=file_path,html_message=html_message,cc_email=cc_email)
    email_obj_2.start()
